package it.kion.util.ui.ulog2;

import java.io.IOException;
import java.io.Serializable;
import java.sql.Timestamp;
import java.util.StringTokenizer;

import org.apache.log4j.spi.LocationInfo;
import org.apache.log4j.spi.LoggingEvent;

import com.sun.org.apache.regexp.internal.RE;
import com.sun.org.apache.regexp.internal.RECompiler;
import com.sun.org.apache.regexp.internal.REProgram;
import com.sun.org.apache.regexp.internal.RESyntaxException;

public class Filter implements Serializable {

	private static final long serialVersionUID = 5396637314837367346L;

	public static enum TYPE {
		MESSAGE,
		TIME,
		LOGGER,
		LINE,
		LEVEL
	};

	private TYPE type;
	
	private String criteria;

	private boolean inclusive;

	private RE mExpression;

	private boolean caseSensitive = false;

	private boolean mRegExpValid;

	public Filter(TYPE type, String criteria, boolean inclusive) {
		init(type, criteria, inclusive);
	}

	public Filter(TYPE type, String criteria, boolean inclusive, boolean caseSensitive) {
		init(type, criteria, inclusive);
		this.caseSensitive = caseSensitive;
	}

	private void init(TYPE type, String criteria, boolean inclusive) {
		this.inclusive = true;
		this.type = type;
		this.criteria = criteria;
		this.inclusive = inclusive;
		try {
			REProgram program = (new RECompiler()).compile(criteria);
			mExpression = new RE(program);
			mRegExpValid = true;
		}
		catch (RESyntaxException _ex) {
			System.out.println("Could not compile regular expression, will use java contains");
			mRegExpValid = false;
		}
	}

	private String getInputValue(LoggingEvent event) {
		return getInputValue(event, type);
	}

	private String getInputValue(LoggingEvent event, TYPE type) {
		String res = event.getRenderedMessage();
		switch (type) {
			case MESSAGE: 
				if(event.getLoggerName().equals(Log4jUtil.KODOSQL)){
					res = SQLFormatter.formatSql(res);
			 	} 
				break;
			
			case LOGGER: res = event.getLoggerName();
				break;
			
			case LEVEL: res = event.getLevel().toString();
				break;
			
			case LINE: 
				LocationInfo locationInformation = event.getLocationInformation();
				res = (locationInformation == null) ? "" : locationInformation.getLineNumber();
				break;
				
			case TIME:
				res = Timestamp.valueOf(String.valueOf((event.timeStamp))).toString();
				break;
		}
				
		return res;
	}

	public boolean isValid(LoggingEvent le) {
		String fromMessage = getInputValue(le);
		String fromFilter = criteria;
		if (mRegExpValid) {
			if (mExpression.match(fromMessage)) {
				return isInclusive();
			}
		}
		else
			return !isInclusive();
		int index = fromMessage.indexOf(fromFilter);
		if (index > -1 && isInclusive()) return true;
		return index == -1 && !isInclusive();
	}

	public boolean isValid(LoggingEvent le, boolean allFields) {
		if (allFields) {
			if (mExpression.match(getInputValue(le, TYPE.MESSAGE))) return true;
			if (mExpression.match(getInputValue(le, TYPE.LEVEL))) return true;
			if (mExpression.match(getInputValue(le, TYPE.LOGGER))) return true;
			if (mExpression.match(getInputValue(le, TYPE.LINE))) return true;
			return mExpression.match(getInputValue(le, TYPE.TIME));
		}
		else {
			return isValid(le);
		}
	}

	public String toString() {
		StringBuffer sb = new StringBuffer();
		switch(type) {
			case LEVEL: 
				sb.append(Log4jUtil.LIVELLO_CAPTION);
				break;
			case LOGGER: 
				sb.append(Log4jUtil.LOGGER_CAPTION);
				break;
			case MESSAGE: 
				sb.append(Log4jUtil.MESSAGGIO_CAPTION);
				break;
			case LINE: 
				sb.append(Log4jUtil.LINEA_CAPTION);
				break;
			case TIME: 
				sb.append(Log4jUtil.DATA_CAPTION);
				break;
		}
		if (inclusive) {
			sb.append(" includes ");
		} else {
			sb.append(" excludes ");
		}
		sb.append(criteria);
		return sb.toString();
	}

	public static String serialize(Filter filter) {
		StringBuffer sb = new StringBuffer();
		sb.append(String.valueOf(filter.getType()));
		sb.append(":::");
		sb.append(filter.getCriteria());
		sb.append(":::");
		sb.append((new Boolean(filter.inclusive)).toString());
		return sb.toString();
	}

	public static Filter deSerialize(String serializedFilter) throws IOException {
		try {
			StringTokenizer st = new StringTokenizer(serializedFilter, ":::");
			int tokenCount = st.countTokens();
			String fields[] = new String[tokenCount];
			for (int i = 0; i < tokenCount; i++) {
				fields[i] = st.nextToken();
			}
			return new Filter(TYPE.valueOf(fields[0].toString()), fields[1], Boolean.valueOf(fields[2]).booleanValue());
		}
		catch (NumberFormatException _ex) {
			throw new IOException("Problemi nella deserializzazione dei filtri!");
		}
	}

	public static Filter composeFromForm(String input) {
		StringTokenizer st = new StringTokenizer(input, " ");
		TYPE type = TYPE.MESSAGE;
		boolean inclusive = true;
		String tmp = st.nextToken();
		/// TODO
//		type = ULogUtilities.convertColumnToInt(tmp);
		tmp = st.nextToken();
		if (tmp.equals("includes")) {
			inclusive = true;
		}
		else {
			inclusive = false;
		}
		String criteria = st.nextToken();
		return new Filter(type, criteria, inclusive);
	}

	public String getCriteria() {
		return criteria;
	}

	public boolean isInclusive() {
		return inclusive;
	}

	public TYPE getType() {
		return type;
	}

	public boolean equals(Object obj) {
		boolean res = false;
		if (obj instanceof Filter) {
			Filter filter = (Filter) obj;
			res =   filter.getType()==getType() 
					&& filter.getCriteria().equalsIgnoreCase(getCriteria()) 
					&& filter.isInclusive() == isInclusive();
		}
		return res;
	}
	
	@Override
	public int hashCode() {
		int res = super.hashCode();
		res = this.toString().hashCode();
		return res;
	}

	public boolean isCaseSensitive() {
		return caseSensitive;
	}

	public void setCaseSensitive(boolean caseSensitive) {
		this.caseSensitive = caseSensitive;
	}
	
}
